[python] 關於python三兩事 - class, __init__, __call__


Posted by mike-hsieh on 2024-01-15

如標題,紀錄一下 python 中有幾個基礎的東西。

__init____call__ 是 Python 中特別定義的類方法,它們屬於一類稱為 "魔術方法"(magic methods)或 "特殊方法"(special methods)的方法。這些方法有特定的命名規則(以雙下劃線開始和結束)和特定的功能。

1. __init__ 方法:

  • __init__ 是一個初始化器(不是構造函數,Python 的構造函數是 __new__,參考下方補充。)。
  • 當創建類的新實例時,__init__ 會自動被調用。
  • 它用於設置對象的初始狀態或初始化其屬性。
  • 這是一個非常常用的方法,用於類的任何初始化操作。
  • 只能定義一個,若有多個,只有最後一個定義的會被使用。

2. __call__ 方法:

  • __call__ 方法使得實例能夠被當作函數來調用。
  • 當實例被當作函數調用時(即像 instance() 這樣),Python 會自動調用該實例的 __call__ 方法。
  • 這個方法的使用在某些情況下非常方便,尤其是在設計像函數一樣行為的對象時。
  • 只能定義一個,若要處理多種情況,使用條件語句來檢查傳入參數的類型或數量,或者使用可變參數(如 *args 和 **kwargs)來處理不同的參數情況。

範例: 建立一個Greeter的類,並且實例化、調用方法。

code: python
# 建立一個Greeter的類
class Greeter:
    # 定義
    def __init__(self, name):
        self.name = name  # 使用 __init__ 方法初始化 name 屬性

    def __call__(self, greet):
        return f"{greet}, {self.name}!"  # 定義 __call__ 方法,使實例可以被調用
code: python
# 初始化 Greeter 實例 
# 創建了一個 Greeter 的實例,將 "Alice" 作為名字傳入。這個名字通過 __init__ 方法設置到實例的 name 屬性上。
greeter = Greeter("Alice")
code: python
# 調用實例方法
# 然後我使用這個實例就像調用一個函數那樣,傳入一個問候語 "Hello"。由於實例有 __call__ 方法,所以它可以被像函數那樣調用。
# __call__ 方法接收這個問候語並返回一個格式化的字符串。
greeting_message = greeter("Hello")  # 調用實例,返回 "Hello, Alice!"


總結:

以上這個例子通過 __init__ 方法設置初始狀態(在這裡是設置名字),並通過 __call__ 方法使得實例的行為像一個函數。這種方式在編寫具有函數特性的類時非常有用。




補充:

  • 關於 __new__:
    在 Python 中,__new__ 是一個特殊的靜態方法,用於創建並返回一個類的新實例。它是在 __init__ 方法之前被調用的,並且是真正的構造函數。然而,在日常 Python 編程中,__new__ 的使用並不常見。它主要在涉及到較為復雜的對象創建過程,或者在創建不可變的數據類型(如 int、str、tuple 等)的子類時才會用到。
# 在這個範例中,Singleton 類使用 __new__ 方法來確保類的實例只被創建一次。
# 每次嘗試創建 Singleton 的新實例時,__new__ 方法都會先檢查是否已經存在一個實例,如果存在,則返回那個已經存在的實例。
code: python
class Singleton:
    _instance = None

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super(Singleton, cls).__new__(cls)
        return cls._instance

    def __init__(self):
        self.value = "Some state"

# 現在,無論你創建多少次 Singleton 的實例,它都只會有一個實例存在。
singleton1 = Singleton()
singleton2 = Singleton()

print(singleton1 == singleton2)  # 這將輸出 True,因為兩個變數引用的是同一個實例。

總的來說,除非你需要控制對象的創建過程(例如實現設計模式如單例模式),否則在日常的 Python 編程中很少需要使用 __new__。大多數時候,只使用 __init__ 方法就足夠了。



  • 可變參數(*args, **kwargs):
code: python
# 創建了一個名為 FlexibleGreeter 的類,它演示了如何在 __call__ 方法中使用 可變參數(*args)和關鍵字參數(**kwargs)

# 定義 迎接人員 Class
class FlexibleGreeter:
    # 定義初始方法,設定招呼用語。
    def __init__(self, greeting):
        self.greeting = greeting

    # 定義實例方法,取得完整招呼。
    def __call__(self, *args, **kwargs):
        # 取得 關鍵字參數 **kwargs
        # 若沒有取得任何參數,預設給 there
        name = kwargs.get('name', 'there')

        # 取得 可變參數
        if args:
            # 對傳入的可變參數,進行 'and' 字串組合
            name = ' and '.join(args)

        # 返回 完整招呼。 
        # 在3.6+版本,可以使用 f-string 字串插入語法 {XXXX},進行高效的字串組合
        return f"{self.greeting}, {name}!"

# 實例化 迎接人員
greeter = FlexibleGreeter("Hello")

# 以下使用實例方法
# 使用 關鍵字參數 **kwargs。 (name="Alice")
greeting1 = greeter(name="Alice")  # 'Hello, Alice!'

# 使用 可變參數 *args。 ("Alice", "Bob")  
greeting2 = greeter("Alice", "Bob")    # 'Hello, Alice and Bob!'

# 不使用參數
greeting3 = greeter()    # 'Hello, there!'

#Python #__init__ #__call__







Related Posts

JS-[Array篇]-入門的Array 方法

JS-[Array篇]-入門的Array 方法

Functional Programming 筆記

Functional Programming 筆記

在 Raspberry Pi 4 使用 TOTOLINK-N150UA-B

在 Raspberry Pi 4 使用 TOTOLINK-N150UA-B


Comments